package com.gitee.jjnnzb.designpatterninspring.common.aop;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.gitee.jjnnzb.designpatterninspring.common.threadlocal.LogThreadLocal;
import com.gitee.jjnnzb.designpatterninspring.common.util.IpUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import java.lang.reflect.Method;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 记录调用Controller的日志
 * @author 28s
 * @since 2023-07-11
 */
@Aspect
@Component
@Slf4j
@Order(0)
public class LogAspect {
    
    @Resource
    private ObjectMapper objectMapper;
    
    @Pointcut("execution(* com.gitee.jjnnzb.designpatterninspring..*.controller.*.*(..))")
    public void log() {
        // sonar
    }
    
    @Before("log()")
    public void before(JoinPoint joinPoint) {
        // 接收到请求，记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (Objects.isNull(attributes)) {
            return;
        }
        //获取请求的request
        HttpServletRequest request = attributes.getRequest();
        //获取所有请求的参数，封装为map对象
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        //获取被拦截的方法
        Method method = methodSignature.getMethod();
        //获取被拦截的方法名
        String methodName = method.getName();
        Object[] args = joinPoint.getArgs();
        //获取所有请求参数key和value
        try {
            String keyValue = objectMapper.writeValueAsString(getRecordableParam(args));
            log.info("""
                            
                            ====>>>> AOP begin ,请求开始方法  :{}.{}()
                            ====>>>> 请求参数 key:value = {}
                            ====>>>> 请求url = {} , {}
                            ====>>>> 请求ip = {}""",
                    method.getDeclaringClass(), methodName, keyValue, request.getMethod(), request.getRequestURL(), IpUtil.getIpAddress(request));
        } catch (Exception e) {
            log.warn("""
                            
                            ====>>>> AOP begin ,请求开始方法  :{}.{}()
                            ====>>>> 请求参数 key:value = {}
                            ====>>>> 请求url = {} , {}
                            ====>>>> 请求ip = {}""",
                    method.getDeclaringClass(), methodName, "", request.getMethod(), request.getRequestURL(), IpUtil.getIpAddress(request));
        }
    }
    
    @AfterReturning(returning = "result", pointcut = "log()")
    public Object afterReturn(Object result) {
        if (result instanceof MultipartFile) {
            result = ((MultipartFile) result).getOriginalFilename();
        }
    
        long startTime = LogThreadLocal.getThreadLocal();
        long endTime = Instant.now().toEpochMilli();
        long callTime = (endTime - startTime);
        try {
            String keyValue = objectMapper.writeValueAsString(result);
            log.info("\n<<<<==== AOP afterReturn,返回值result = {}\n<<<<==== 调用controller花费时间time = {} ms", keyValue, callTime);
        } catch (Exception e) {
            log.info("\n<<<<==== AOP afterReturn,返回值result = {}\n<<<<==== 调用controller花费时间time = {} ms", result, callTime);
        }
        LogThreadLocal.remove();
        return result;
    }
    
    @AfterThrowing(pointcut = "log()", throwing = "ex")
    public void afterThrowing(Exception ex) {
        // sonar
        long startTime = LogThreadLocal.getThreadLocal();
        long endTime = Instant.now().toEpochMilli();
        long callTime = (endTime - startTime);
        log.warn("\n<<<<==== AOP afterReturn,错误信息message = {}\n<<<<==== 调用controller花费时间time = {} ms", ex.getMessage(), callTime);
        LogThreadLocal.remove();
    }
    
    private List<Object> getRecordableParam(Object[] args) {
        ArrayList<Object> objects = new ArrayList<>(args.length);
        for (Object object : args) {
            if (object instanceof HttpServletRequest ||
                    object instanceof HttpServletResponse ||
                    object instanceof MultipartFile) {
                continue;
            }
            objects.add(object);
        }
        return objects;
    }
}